#pragma once

#include "msgq/msgq.h"
#include <cstring>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>

class MsgQ {
public:
  explicit MsgQ(const std::string &path, size_t size = DEFAULT_SEGMENT_SIZE);
  ~MsgQ();

  MsgQ(const MsgQ &) = delete;
  MsgQ &operator=(const MsgQ &) = delete;
  MsgQ(MsgQ &&) noexcept;
  MsgQ &operator=(MsgQ &&) noexcept;

  void init_publisher();

  bool send(const void *data, size_t size);
  bool send(const std::string &message);

  template <typename T> bool send(const T &data) {
    return send(&data, sizeof(T));
  }

  bool all_readers_updated() const;

  void init_subscriber();

  bool receive(std::vector<uint8_t> &data);
  bool receive(std::string &message);

  template <typename T> bool receive(T &data) {
    std::vector<uint8_t> buffer;
    if (receive(buffer) && buffer.size() == sizeof(T)) {
      std::memcpy(&data, buffer.data(), sizeof(T));
      return true;
    }
    return false;
  }

  bool is_message_ready() const;

  void reset_reader();

private:
  std::unique_ptr<msgq_queue_t, void (*)(msgq_queue_t *)> queue_;
  std::string endpoint_;
  bool is_publisher_;
  bool is_initialized_;
};

class MsgData {
public:
  MsgData() : msg_{0, nullptr} {}
  explicit MsgData(size_t size);
  MsgData(const void *data, size_t size);
  explicit MsgData(const std::string &str);

  ~MsgData();

  MsgData(const MsgData &) = delete;
  MsgData &operator=(const MsgData &) = delete;
  MsgData(MsgData &&other) noexcept;
  MsgData &operator=(MsgData &&other) noexcept;

  void *data() { return msg_.data; }
  const void *data() const { return msg_.data; }
  size_t size() const { return msg_.size; }

  std::string to_string() const;

  msgq_msg_t *get() { return &msg_; }
  const msgq_msg_t *get() const { return &msg_; }

  bool empty() const { return msg_.size == 0 || msg_.data == nullptr; }

private:
  msgq_msg_t msg_;
};

inline std::shared_ptr<MsgQ>
make_shared_msgq(const std::string &path, size_t size = DEFAULT_SEGMENT_SIZE) {
  return std::make_shared<MsgQ>(path, size);
}
